package main

import (
	"container/list"
	"context"
	"fmt"
	"net/http"
	"os/exec"
	"strconv"
	"sync"
	"time"
)

var dname = "lua5.1.dll"

//读取配置文件并保存到结构体
type ServerConfig struct {
	initFile    string //初始化文件路径
	typeStr     string //服务器类型
	addr        string //监听ip和端口
	webView     string //webview 执行文件路径
	webTitle    string //webView标题
	webPath     string //webview打开路径
	stripPrefix string //静态资源前缀
	filePath    string //静态资源目录
	vmSize      int
}

var myConf ServerConfig

//封装lua获取配置文件的方法
func getLuaConfig(lua *Lua, key string) string {
	_, res, _ := lua.Lua_getglobal(key)
	if res != 0 {
		de, _, _ := lua.Lua_tostring(-1)
		return string(PtrByte(de))
	}
	lua.lua_pop(1)
	return ""
}

//服务相关配置加载
func loadConfig() int {
	var code int = 0
	lua := Init(dname)
	lua.Lua_newstate()
	lua.LuaL_openlibs()
	df, _, _ := lua.LuaL_dofile("server-conf.lua")
	if df == 0 {
		myConf.initFile = getLuaConfig(lua, "initFile")
		myConf.webTitle = getLuaConfig(lua, "webTitle")
		myConf.webView = getLuaConfig(lua, "webView")
		myConf.webPath = getLuaConfig(lua, "webPath")

		myConf.stripPrefix = getLuaConfig(lua, "stripPrefix")
		if myConf.stripPrefix == "" {
			myConf.stripPrefix = "/static/"
		}
		myConf.filePath = getLuaConfig(lua, "filePath")
		if myConf.filePath == "" {
			myConf.filePath = "html/"
		}

		myConf.typeStr = getLuaConfig(lua, "type")
		if myConf.typeStr == "" {
			myConf.typeStr = "single"
		}

		myConf.addr = getLuaConfig(lua, "addr")
		if myConf.addr == "" {
			myConf.addr = ":80"
		}

		size := getLuaConfig(lua, "vmSize")
		d, e := strconv.Atoi(size)
		if e != nil {
			myConf.vmSize = 10
		} else {
			myConf.vmSize = d
		}

		code = 1
	} else {
		s, _, _ := lua.Lua_tostring(-1)
		fmt.Println("错误:", string(PtrByte(s)))
	}
	lua.Lua_close()
	return code
}

func luaHandler(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	r.ParseMultipartForm(1024 * 1024)
	path := r.URL.Path
	name := path[1:len(path)]
	if name == "favicon.ico" {
		w.Write([]byte("lll"))
		return
	}
	fileName := "lua/" + name + ".lua"
	lua := luaStatePop()

	lua.Lua_pushstring(r.URL.String())
	lua.Lua_setglobal("url")
	lua.Lua_pushstring(r.URL.Path)
	lua.Lua_setglobal("path")

	var form map[string][]string
	if r.Method == "POST" {
		form = r.PostForm
	} else {
		form = r.Form
	}

	//设置请求表单
	lua.Lua_newtable()
	for k, v := range form {
		if v == nil || v[0] == "" || k == "" {
			continue
		}
		lua.Lua_pushstring(k)
		lua.Lua_pushstring(v[0])
		lua.Lua_settable(-3)
	}
	lua.Lua_setglobal("param")
	//设置请求头
	val := r.Header
	lua.Lua_newtable()
	for k, v := range val {
		if v == nil || v[0] == "" || k == "" {
			continue
		}
		lua.Lua_pushstring(k)
		lua.Lua_pushstring(v[0])
		lua.Lua_settable(-3)
	}
	lua.Lua_setglobal("header")

	df, _, _ := lua.LuaL_dofile(fileName)
	if df != 0 {
		s, _, _ := lua.Lua_tostring(-1)
		fmt.Println("错误:", string(PtrByte(s)))
	} else {
		var data []byte = []byte("ok")
		//获取返回值
		res, _, _ := lua.Lua_getglobal("response")
		if res != 0 {

			//获取返回结果
			bdfileld, _, _ := lua.Lua_getfield(-1, "body")
			if bdfileld != 0 {
				body, _, _ := lua.Lua_tostring(-1)
				data = PtrByte(body)
			}
			lua.lua_pop(1)

			//获取返回响应头
			hdfileld, _, _ := lua.Lua_getfield(-1, "header")
			if hdfileld != 0 {
				lua.Lua_pushnil()
				for {
					n, _, _ := lua.Lua_next(-2)
					//为空退出
					if n == 0 {
						break
					}
					tb := lua.Lua_istable(-1)
					//当不是表格结构时候
					if tb == 0 {
						hk, _, _ := lua.Lua_tostring(-2)
						nm := string(PtrByte(hk))
						lua.lua_pop(1)
						nmr, _, _ := lua.Lua_getfield(-2, nm)
						if nmr != 0 {
							hv, _, _ := lua.Lua_tostring(-1)
							if hv != 0 {
								val := string(PtrByte(hv))
								w.Header().Set(nm, val)

							}
						}
						lua.lua_pop(1)
					} else {
						lua.lua_pop(1)
					}

				}

			}

			lua.lua_pop(1)

			//获取返回码,应该在设置响应头后面
			cdfild, _, _ := lua.Lua_getfield(-1, "code")
			if cdfild != 0 {
				code, _, _ := lua.Lua_tointeger(-1)
				//code不能为0 或者其他码,不然报错
				//fmt.Println("code::", code)
				if code != 0 {
					w.WriteHeader(int(code))
				}

			}
			lua.lua_pop(1)

		}
		lua.lua_pop(1)
		w.Write(data)
	}

	luaStatePush(lua)

}

//定义一个lua虚拟机列表
var luaList *list.List

func luaInit(num int) int {
	luaList = list.New()
	for i := 0; i < num; i++ {
		var luaState = Init(dname)
		luaState.Lua_newstate()
		luaState.LuaL_openlibs()
		//下面针对文件进行配置
		if myConf.initFile != "" {
			df, _, _ := luaState.LuaL_loadfile(myConf.initFile)
			if df != 0 {
				s, _, _ := luaState.Lua_tostring(-1)
				fmt.Println("初始化文件出错:", string(PtrByte(s)))
				return 1
			}
		}
		//luaState.LuaL_dostring(ll)

		luaList.PushBack(luaState)
	}
	return 0
}

var mux sync.Mutex

func luaStatePush(luaState *Lua) {
	mux.Lock()
	luaList.PushBack(luaState)
	mux.Unlock()
}

func luaStatePop() *Lua {
	mux.Lock()
	if luaList.Len() == 0 {
		mux.Unlock()
		return nil
	}
	luaState := luaList.Front()
	luaList.Remove(luaState)
	mux.Unlock()
	ll := luaState.Value
	val, _ := ll.(*Lua)
	return val
}

func runFile(name string) {
	lua := Init(dname)
	lua.Lua_newstate()
	lua.LuaL_openlibs()
	df, _, _ := lua.LuaL_dofile(name)
	if df != 0 {
		s, _, _ := lua.Lua_tostring(-1)
		fmt.Println("错误:", string(PtrByte(s)))
	}
	lua.Lua_close()
}

func main() {
	loadConfig()
	var cmd *exec.Cmd
	if myConf.typeStr == "server" {
		fmt.Println("-------start server-----------")
		fmt.Println("address:", myConf.addr)
		fmt.Println("vm size:", myConf.vmSize)
		r := luaInit(myConf.vmSize)
		if r == 1 {
			return
		}

		fs := http.FileServer(http.Dir(myConf.filePath))
		http.Handle(myConf.stripPrefix, http.StripPrefix(myConf.stripPrefix, fs))
		http.HandleFunc("/", luaHandler)
		serv := http.Server{Addr: myConf.addr}

		if myConf.webView != "" {
			//path := "G:/Desktop/webview/MyWeb.exe"
			cmd = exec.Command(myConf.webView, myConf.webPath, myConf.webTitle)
			cmd.Start()

			out, erro := cmd.Output()
			if erro != nil {
				fmt.Println(erro, out)
			}
			//主页面退出后，程序也跟着退出
			go func() {
				cmd.Wait()
				fmt.Println("关闭")
				ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
				defer cancel()
				serv.Shutdown(ctx)
			}()
		}

		err := serv.ListenAndServe()
		if err != nil {
			fmt.Println(err)
			//当启动失败时候
			cmd.Process.Kill()
		}

		// err := http.ListenAndServe(myConf.addr, nil)
		// if err != nil {
		// 	fmt.Println(err)
		// }

	} else if myConf.typeStr == "single" {
		runFile(myConf.initFile)
	} else {
		fmt.Println("-------Please set the correct type-----------")
	}

}
